export const linear = v => v

function cubicBezier(p1x, p1y, p2x, p2y) {
  var ZERO_LIMIT = 1e-6;
  // Calculate the polynomial coefficients,
  // implicit first and last control points are (0,0) and (1,1).
  var ax = 3 * p1x - 3 * p2x + 1,
      bx = 3 * p2x - 6 * p1x,
      cx = 3 * p1x;

  var ay = 3 * p1y - 3 * p2y + 1,
      by = 3 * p2y - 6 * p1y,
      cy = 3 * p1y;

  function sampleCurveDerivativeX(t) {
      // `ax t^3 + bx t^2 + cx t' expanded using Horner 's rule.
      return (3 * ax * t + 2 * bx) * t + cx;
  }

  function sampleCurveX(t) {
      return ((ax * t + bx) * t + cx ) * t;
  }

  function sampleCurveY(t) {
      return ((ay * t + by) * t + cy ) * t;
  }

  // Given an x value, find a parametric value it came from.
  function solveCurveX(x) {
      var t2 = x,
          derivative,
          x2;

      // https://trac.webkit.org/browser/trunk/Source/WebCore/platform/animation
      // First try a few iterations of Newton's method -- normally very fast.
      // http://en.wikipedia.org/wiki/Newton's_method
      for (var i = 0; i < 8; i++) {
          // f(t)-x=0
          x2 = sampleCurveX(t2) - x;
          if (Math.abs(x2) < ZERO_LIMIT) {
              return t2;
          }
          derivative = sampleCurveDerivativeX(t2);
          // == 0, failure
          if (Math.abs(derivative) < ZERO_LIMIT) {
              break;
          }
          t2 -= x2 / derivative;
      }

      // Fall back to the bisection method for reliability.
      // bisection
      // http://en.wikipedia.org/wiki/Bisection_method
      var t1 = 1,
          t0 = 0;
      t2 = x;
      while (t1 > t0) {
          x2 = sampleCurveX(t2) - x;
          if (Math.abs(x2) < ZERO_LIMIT) {
              return t2;
          }
          if (x2 > 0) {
              t1 = t2;
          } else {
              t0 = t2;
          }
          t2 = (t1 + t0) / 2;
      }

      // Failure
      return t2;
  }

  function solve(x) {
      return sampleCurveY(solveCurveX(x));
  }

  return solve;
}

export const ease = cubicBezier(.25, .1, .25, 1)
export const easeIn = cubicBezier(.42, 0, 1, 1)
export const easeOut = cubicBezier(0, 0, .58, 1)
export const easeInOut = cubicBezier(.42, 0, .58, 1)